﻿namespace Nomo.UnityCoreModule.AI
{
    /// <summary>
    /// 漂浮物。
    /// </summary>
    public abstract class Floater : UnityEngine.MonoBehaviour
    {
        /// <summary>
        /// 正在漂浮吗？
        /// </summary>
        public bool floating = true;

        /// <summary>
        /// 漂浮速度。
        /// </summary>
        [UnityEngine.Min(0F)] public float speed;

        /// <summary>
        /// 坐标系。
        /// </summary>
        /// <remarks>
        /// 不要在运行时在 Inspector 窗口里修改这个序列化字段的值。
        /// </remarks>
        [UnityEngine.SerializeField] protected UnityEngine.Space space;

        /// <summary>
        /// 初始位置。
        /// </summary>
        protected UnityEngine.Vector3 Origin;

        /// <summary>
        /// 目标位置。
        /// </summary>
        protected UnityEngine.Vector3 Target;

        /// <summary>
        /// 获取或设置当前位置。
        /// </summary>
        protected UnityEngine.Vector3 Current
        {
            get
            {
                switch (space)
                {
                    case UnityEngine.Space.World:
                        return transform.position;
                    case UnityEngine.Space.Self:
                        return transform.localPosition;
                    default:
                        throw new System.ArgumentOutOfRangeException();
                }
            }
            set
            {
                switch (space)
                {
                    case UnityEngine.Space.World:
                        transform.position = value;
                        break;
                    case UnityEngine.Space.Self:
                        transform.localPosition = value;
                        break;
                    default:
                        throw new System.ArgumentOutOfRangeException();
                }
            }
        }

        /// <summary>
        /// 应该更新目标位置吗？
        /// </summary>
        protected abstract bool ShouldUpdateTarget { get; }

        /// <summary>
        /// 获取或修改坐标系。
        /// </summary>
        public UnityEngine.Space Space
        {
            get => space;
            set
            {
                if (value == space)
                {
                    return;
                }
                space = value;
                UnityEngine.Vector3 offset;
                switch (value)
                {
                    case UnityEngine.Space.World:
                        offset = transform.localPosition - transform.position;
                        break;
                    case UnityEngine.Space.Self:
                        offset = transform.position - transform.localPosition;
                        break;
                    default:
                        throw new System.ArgumentOutOfRangeException();
                }
                Origin += offset;
                Target += offset;
            }
        }

        /// <summary>
        /// 设置当前位置为初始位置。
        /// </summary>
        public void SetCurrentToOrigin()
        {
            Current = Origin;
        }

        /// <summary>
        /// 设置初始位置为当前位置。
        /// </summary>
        public void SetOriginToCurrent()
        {
            Origin = Current;
        }

        /// <summary>
        /// 检查当前状态是否合法。如果合法，则会使 <see cref="Update"/> 中的逻辑继续向下执行。
        /// </summary>
        /// <returns>当前状态合法吗？</returns>
        /// <remarks>
        /// 在获取这个值之前，<see cref="floating"/> 和 <see cref="speed"/> 已经通过了检测，所以不要再对这两个成员进行检测。
        /// </remarks>
        protected virtual bool StateValid => true;

        /// <summary>
        /// 更新目标位置。
        /// </summary>
        public abstract void UpdateTarget();

        /// <summary>
        /// 漂浮。
        /// </summary>
        protected abstract void OnFloat();

        /// <summary>
        /// 在此组件所在的游戏物体从未激活状态转变为激活状态时执行。
        /// </summary>
        protected virtual void OnEnable()
        {
            SetOriginToCurrent();
            UpdateTarget();
        }

        /// <summary>
        /// 在此组件所在的游戏物体从激活状态转变为未激活状态时执行。
        /// </summary>
        protected virtual void OnDisable()
        {
            SetCurrentToOrigin();
        }

        private void Update()
        {
            if (!floating || speed <= 0F || !StateValid)
            {
                return;
            }
            if (ShouldUpdateTarget)
            {
                UpdateTarget();
            }
            OnFloat();
        }
    }
}
